using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Media;
using System.Windows.Shapes;

using SvnCore;

using SVNTreeViewer.Views;

namespace SVNTreeViewer.VisualTreeBuilder
{
    /// <summary>
    /// Paints the connection between two points in 2D space.
    /// </summary>
    public class ConnectionPainter
    {
        private readonly Brush strokeColor = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FF89C3E8"));

        private readonly List<ConnectionDetails> connections = new List<ConnectionDetails>();

        /// <summary>
        /// Draws the connection.
        /// </summary>
        /// <param name="from">From.</param>
        /// <param name="to">To.</param>
        /// <returns>Returns the <see cref="UIElement"/> to add to canvas.</returns>
        public UIElement DrawConnection(RevisionBlock from, RevisionBlock to)
        {
            return from.Location.Y < to.Location.Y
                       ? this.DrawConnectionDown(from.Location, to.Location, to.Revision.Number)
                       : this.DrawConnectionUp(from.Location, to.Location, to.Revision.Number);
        }

        private static bool DoLinesIntersect(Point existingPointStart, Point existingPointEnd, Point newPointStart, Point newPointEnd)
        {
            return GetIntersectPoint(existingPointStart, existingPointEnd, newPointStart) !=
                   GetIntersectPoint(existingPointStart, existingPointEnd, newPointEnd) ||
                   GetIntersectPoint(newPointStart, newPointEnd, existingPointStart) !=
                   GetIntersectPoint(newPointStart, newPointEnd, existingPointEnd);
        }

        private static double GetIntersectPoint(Point p1, Point p2, Point p3)
        {
            return ((p2.X - p1.X) * (p3.Y - p1.Y)) - ((p3.X - p1.X) * (p2.Y - p1.Y));
        }

        private UIElement DrawConnectionDown(Point p1, Point p2, long destination)
        {
            int step = this.GetConnectionDepth(p1, p2, destination);

            Polyline black = new Polyline
                {
                    Stroke = this.strokeColor,
                    StrokeThickness = 3,
                    Points = new PointCollection()
                        {
                            p1,
                            new Point(p1.X, p2.Y - step - 20),
                            new Point(p2.X, p2.Y - step - 20),
                            p2
                        }
                };

            this.connections.Add(new ConnectionDetails()
                {
                    Depth = p2.Y - step,
                    StartPoint = p1,
                    EndPoint = p2,
                    Destination = destination
                });

            return black;
        }

        private UIElement DrawConnectionUp(Point p1, Point p2, long destination)
        {
            return new UIElement();

            int step = this.GetConnectionDepth(p1, p2, destination);

            Polyline black = new Polyline
            {
                Stroke = this.strokeColor,
                StrokeThickness = 3,
                Points = new PointCollection()
                        {
                            p1,
                            new Point(p1.X, p2.Y + step + 20),
                            new Point(p2.X, p2.Y + step + 20),
                            p2
                        }
            };

            this.connections.Add(new ConnectionDetails()
            {
                Depth = p2.Y + step,
                StartPoint = p1,
                EndPoint = p2,
                Destination = destination
            });

            return black;
        }

        private int GetConnectionDepth(Point p1, Point p2, long destination, int step = 0)
        {
            ConnectionDetails colision =
                this.connections.FirstOrDefault(
                    x =>
                    x.Depth == p2.Y - step
                    && DoLinesIntersect(x.StartPoint, x.EndPoint, p1, p2)
                    && x.Destination != destination);

            return colision != null ? 20 + this.GetConnectionDepth(p1, new Point(p2.X, p2.Y), destination, 20 + step) : 0;
        }
    }
}